<?php
/**
 * デフォルトのリクエスト解析モジュール
 * 兼
 * アクセスログ取得・解析モジュール
 *  modRequest
 */
class Request {
	private $LogFlg;

	private $SERVER;

	/**
	 * アクセスカウンター
	 */
	public $Counter;

	private $AgentType;

	public function __construct() {
		$this->AgentType = null;
		$this->LogFlg = false;
	}

	public function init () {
		Y('this.URI+',  @$_SERVER['REQUEST_URI']);
		Y('this.POST+', @$_POST);
		Y('this.GET+',  @$_GET);
		Y('this.SERVER+',  @$_SERVER);

		// TODO: リモートホスト強制取得になっているので、設定によって変更できるようにすること。
		if (!Y('this.SERVER.REMOTE_HOST') && !empty($_SERVER['REMOTE_ADDR'])) Y('this.SERVER.REMOTE_HOST+', gethostbyaddr($_SERVER['REMOTE_ADDR']));

		Y('this.global+', YuTori::Create($this, 'requestGlobal'));
		$this->initGlobal();

		Y('this.AgentType+',  YuTori::Create($this, 'AgentType'));

		Y('this.getCounter+', YuTori::Create($this, 'getCounter'));


		Y('this.Admin+', YuTori::Create($this, 'callAdmin'));
		Y('this.getRaw+', YuTori::Create($this, 'getRaw'));
	}

	/**
	 * コールされるとアクセスログを記録する。
	 * 複数コールしても記録は一度しか行わない。
	 */
	public function call () {
		if ($this->LogFlg) return true;

		// SERVERがない時はそのまま。
		if (!count($this->SERVER)) {
			$this->LogFlg = true;
			return true;
		}
		// index.ymlで指定がない時もそのまま。
		if (!Y('App.Module.Request.AccessLog')) {
			$this->LogFlg = true;
			return true;
		}
		
		$Log = Y('Request.SERVER');
		
		$Query =array (
			'query'=>'insert',
			'table'=>'accesslog_log',
			'data'=>$Log,
			);
		
		Y('Helper.loadAction', 'ConnectDB', $Query);

		if (Y('App.Module.Request.Counter')) {
	
			// アクセスカウンタの処理
			Y('Logger.Debug','アクセスカウンタ処理開始');
			
			// リファラが別ドメインの場合は確実に+1
			if ($this->IsNewAccessFromReferer()) {
				$this->addCounter();
			} elseif (empty($this->SERVER['HTTP_REFERER'])) {
				// リファラがNOT_GETの時はアクセスログから判断する
				// インターバルの取得
				if (!$Interval = Y('App.Module.AccessLog.Interval')) $Interval = 3600;

				$Query =array (
					'query'=>'select',
					'table'=>'accesslog_log',
					'column'=>'count(ID) as count',
					'where'=>'`REQUEST_TIME`>'.($this->SERVER['REQUEST_TIME'] - $Interval).' AND `REMOTE_ADDR`="'.$this->SERVER['REMOTE_ADDR'].'" AND `HTTP_USER_AGENT`="'.$this->SERVER['HTTP_USER_AGENT'].'"',
					);
				
				Y('Helper.list.ConnectDB', $Query);
	
				$IsOnAccess = Y('Helper.list.ConnectDB.COM.Result');
				// 今回のアクセスが確実にヒットするので、1以下の時がtrue
				if ((int)$IsOnAccess['count'] <= 1) {
					Y('Logger.Debug','新規アクセス判定(一定期間アクセスなし)');
					$this->addCounter();
				}
			}
		}
		$this->LogFlg = true;
		return true;
	}

	/***************** リクエスト解析系 *****************/

	/**
	 * グローバルリクエストの初期化
	 */
	public function initGlobal () {
		if (!isset($_SERVER['REQUEST_URI'])) return true;
		// リクエストURIを計算
		$BaseURI = Y('App.Config.Site.BaseURL');
		// GETクエリを排除
		list($rURI) = explode('?', $_SERVER['REQUEST_URI'], 2);
		// DIR_BASE_HTML以降のみを見る。
		$BaseURI = rtrim ($BaseURI, '/').'/'; $rURI = '/'.ltrim ($rURI, '/');
		$Gap = (strlen($rURI) < strlen($BaseURI))?(strlen($rURI)):(strlen($BaseURI));
		$cURI = $rURI;
		Y('Logger.Debug', '比較開始 '.$BaseURI. ' ＆ '.$rURI);
		for ($i = 2; $i <= $Gap; $i++) {
			$Str1 = substr($BaseURI, strlen($BaseURI) - $i);
			$Str2 = substr($rURI, 0, $i);
			if (0 === strcmp($Str1, $Str2)) {
				$cURI = substr($rURI, $i);
				break;
			}
		}

		$cURI = trim($cURI, '/');
		$expURI = explode ('/', $cURI, 3);
		$GlobalRequest = array (
				'mod'=>(isset($expURI[0]))?($expURI[0]):(''),
				'act'=>(isset($expURI[1]))?($expURI[1]):(''),
				'op' =>(isset($expURI[2]))?($expURI[2]):(''),
				'uri'=>$cURI,
			);
		Y('this.global.mod+', $GlobalRequest['mod']);
		Y('this.global.act+', $GlobalRequest['act']);
		Y('this.global.op+',  $GlobalRequest['op'] );
		Y('this.global.uri+', $GlobalRequest['uri'] );

		if (Y('Logger.Debug$')) Y('Logger.Debug','グローバルリクエスト解析 [mod]="'.$GlobalRequest['mod'] .'" [act]="'.$GlobalRequest['act'] .'" [op]="'.$GlobalRequest['op']. '"');
		return;
	}

	/**
	 * グローバルリクエストの解析
	 * コールするとmod、act、opを一つ左にシフトする
	 * もともとのmodは失われる。
	 */
	public function requestGlobal () {
		$GlobalRequest['mod'] = Y('this.act');
		$exp = explode ('/', Y('this.op'), 2);
		$GlobalRequest['act'] = trim($exp[0], '/');
		$GlobalRequest['op']  = isset($exp[1])?($exp[1]):(null);

		Y('this.mod+', $GlobalRequest['mod']);
		Y('this.act+', $GlobalRequest['act']);
		Y('this.op+',  $GlobalRequest['op'] );
		Y('Logger.Debug','グローバルリクエストシフト [mod]="'.$GlobalRequest['mod'] .'" [act]="'.$GlobalRequest['act'] .'" [op]="'.$GlobalRequest['op']. '"');
		return;
	}

	/**
	 * UAからPC、携帯電話を判別する
	 * TODO: スマートフォンも判別すべし。
	 */
	public function AgentType () {
		if ($this->AgentType !== null) return $this->AgentType;
		
		// 機種判別と固体識別番号の取得
		$agent = $_SERVER['HTTP_USER_AGENT'];

		if(preg_match('/^DoCoMo/', $agent)){
			Y('Request.AgentCareer+', 'DoCoMo');
			$this->AgentType = 'Mobile';
			if (isset($_SERVER["HTTP_X_DCMGUID"])) {
				Y('Request.MobileSubsucribe+', $_SERVER["HTTP_X_DCMGUID"]);
			}

		// J-Phone、Vodafone、SoftBank
		}elseif(preg_match('/^J-PHONE|^Vodafone|^SoftBank/', $agent)){
			Y('Request.AgentCareer+', 'SoftBank');
			$this->AgentType = 'Mobile';
			if (isset($_SERVER["HTTP_X_JPHONE_UID"])) {
				Y('Request.MobileSubsucribe+', $_SERVER["HTTP_X_JPHONE_UID"]);
			}

		// au
		}elseif(preg_match('/^UP.Browser|^KDDI/', $agent)){
			Y('Request.AgentCareer+', 'au');
			$this->AgentType = 'Mobile';
			if (isset($_SERVER["HTTP_X_UP_SUBNO"])) {
				Y('Request.MobileSubsucribe+', $_SERVER["HTTP_X_UP_SUBNO"]);
			}

		// WILLCOM
		}elseif(preg_match('/^PDXGW|DDIPOCKET|WILLCOM/', $agent)) {
			Y('Request.AgentCareer+', 'WILLCOM');
			$this->AgentType = 'PHS';

		// Eモバイル
		}elseif (preg_match('/^emobile/', $agent)) {
			Y('Request.AgentCareer+', 'EMobile');
			$this->AgentType = 'Mobile';
			if (isset($_SERVER['HTTP_X_EM_UID'])) {
				Y('Request.MobileSubsucribe+', $_SERVER['HTTP_X_EM_UID']);
			}

		// 全てに該当しない場合
		}else {
			$this->AgentType = 'PC';
		}
		
		return $this->AgentType;
	}


	/***************** アクセス解析系 ****************/

	/**
	 * リファラから新規アクセスか判定する
	 */
	public function IsNewAccessFromReferer () {
		// リファラがない時はfalse
		if (empty ($this->SERVER['HTTP_REFERER'])) return false;

		// リファラが絶対アドレスでない時はfalse
		if (0 === strncmp ('http://', $this->SERVER['HTTP_REFERER'], 7)) {
			// リファラが自サイトのドメインでない時はtrue
			if (0 === strncmp (Y('App.Site.BaseURL'), $this->SERVER['HTTP_REFERER'], strlen(Y('App.Site.BaseURL')) )) {
				return false;
			} else {
				Y('Logger.Debug','新規アクセス判定(リファラが別サイト)');
				return true;
			}
		} else {
			return false;
		}
	}

	/**
	 * アクセスカウントの取得
	 */
	function getCounter () {
		if (empty($this->Counter)) {
			$Query =array (
				'query'=>'select',
				'table'=>'accesslog_counter',
				'where'=>'ID=1',
				);
			
			Y('Helper.loadAction', 'ConnectDB', $Query);
			$this->Counter = Y('Helper.list.ConnectDB.com.Result');
		}
		return $this->Counter;
	}

	/**
	 * アクセスカウンターの増加処理
	 */
	function addCounter () {
		Y('Logger.Debug','カウンタ増加処理開始');
		// 現在のアクセス数の取得
		$Count = $this->getCounter();
		
		// 最終アクセスがいつかを判定
		$Elapse = date('Y', $this->SERVER['REQUEST_TIME']) - date('Y',$Count['UpdateTime']);
		if (!$Elapse) $Elapse = date('z', $this->SERVER['REQUEST_TIME']) - date('z',$Count['UpdateTime']);
		
		if ($Elapse == 1) {
			$Count['Yesterday'] = $Count['Today'];
			$Count['Today'] = 0;
		} elseif ($Elapse > 1) {
			$Count['Yesterday'] = 0;
			$Count['Today'] = 0;
		}
	
		// アクセス数を+1
		$Count['Today']++;
		$Count['All']++;
		
		$Count['UpdateTime'] = time();
		
		$this->Counter = $Count;
		Y('Helper.loadAction', 'ConnectDB', array(
			'query'=>'update',
			'table'=>'accesslog_counter',
			'where'=>'ID=1',
			'data'=>$Count,
		));
	}

	/**
	 * Admin動作
	 * ログ解析ほか
	 */
	public function callAdmin ($Act) {
		// modLoginより早く動作するため、init時にはパーミッションの確認ができない。
		if (Y('Module.List.Login.User.Permission') != 'admin') return null;

		Y('Output.Body', 'AccessLog:AccessLog/'.$Act);
		return true;
	}

	/**
	 * 日別ログを取得する
	 */
	public function getRaw () {
		$Args = array (0,0,0);
		if ($i = (int)Y('Request.GET.month')) $Args[] = $i; else $Args[] = (int) date('m');
		if ($i = (int)Y('Request.GET.day')) $Args[] = $i;   else $Args[] = (int) date('d');
		if ($i = (int)Y('Request.GET.year')) $Args[] = $i;  else $Args[] = (int) date('Y');

		$StartTime = call_user_func_array ('mktime', $Args);
		$Args[0] = 23; $Args[1] = 59; $Args[2] = 59; 
		$EndTime = call_user_func_array ('mktime', $Args);
		
		$Query =array (
			'query'=>'select',
			'table'=>'accesslog_log',
			'where'=>'REQUEST_TIME >= '.$StartTime.' AND REQUEST_TIME <='.$EndTime,
			);
		
		Y('Helper.loadAction', 'ConnectDB', $Query);
		return Y('Helper.list.ConnectDB.COM.ResultAll');
	}
}
?>
